﻿using System.Collections.Generic;

namespace System.Linq.Functional
{
    public sealed class PatternMatch<T, TResult>
    {
        private readonly T value;
        private readonly List<Tuple<Predicate<T>, Func<T, TResult>>> cases
            = new List<Tuple<Predicate<T>, Func<T, TResult>>>();
        private Func<T, TResult> elseFunc;

        internal PatternMatch(T value)
        {
            this.value = value;
        }

        public PatternMatch<T, TResult> With(Predicate<T> pred, Func<T, TResult> action)
        {
            cases.Add(Tuple.New(pred, action));
            return this;
        }

        public PatternMatch<T, TResult> With(Predicate<T> pred, TResult result)
        {
            Func<T, TResult> action = t => result;
            cases.Add(Tuple.New(pred, action));
            return this;
        }

        public PatternMatch<T, TResult> Else(Func<T, TResult> action)
        {
            if (elseFunc != null)
                throw new InvalidOperationException("Cannot have multiple else cases");

            elseFunc = action;
            return this;
        }

        public TResult Do()
        {
            if (elseFunc != null)
                cases.Add(
                    Tuple.New<Predicate<T>, Func<T, TResult>>(x => true, elseFunc));
            foreach (var item in cases)
                if (item.Item1(value))
                    return item.Item2(value);

            throw new MatchNotFoundException("Incomplete pattern match");
        }
    }
}
